package net.moonbiter.ebs;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

public class ServicesRegister {
	private static final Logger logger = Logger.getLogger(ServicesRegister.class);
	
	private Map<String,ServiceProviderDef> servicesProviders;
	
	public ServicesRegister(){
		servicesProviders = new HashMap<String,ServiceProviderDef>();
	}
	
	public void setServicesProviders(List<Object> servicesProvidersValue){
		logger.debug("setting services list with size "+servicesProvidersValue.size());
		servicesProviders.clear();
		for (Object obj : servicesProvidersValue){
			register(obj);
		}
	}
	
	public void register(Object serviceInstance) {
		logger.debug("Registering a new services provider");
		Class<?> serviceClass = serviceInstance.getClass();
		logger.debug("Services provider class is "+serviceClass.getCanonicalName());
		if (!serviceClass.isAnnotationPresent(BusinessProvider.class)) {
			throw new IllegalArgumentException(
					"A service class must have the BusinessProvider annotation (class:"+serviceClass.getCanonicalName()+")");
		}
		BusinessProvider bp = (BusinessProvider) serviceClass
				.getAnnotation(BusinessProvider.class);
		String providerName = bp.value();
		
		// if not specified the provider name is the class name
		if (providerName.equals(BusinessProvider.CLASS_NAME)){
			providerName = serviceClass.getSimpleName();
		}
		
		logger.debug("Registering provider \""+providerName+"\"");
		
		ServiceProviderDef provider = new ServiceProviderDef();
		
		for (Method method : serviceClass.getMethods()){
			if (method.isAnnotationPresent(BusinessService.class)){
				BusinessService service = (BusinessService)method.getAnnotation(BusinessService.class);
				String serviceName = service.value();
				
				// if not specified the service name is the method name
				if (serviceName.equals(BusinessService.METHOD_NAME)){
					serviceName = method.getName();
				}
				
				String completeServiceName = providerName+"."+serviceName;
				logger.debug("In provider \""+providerName+"\" found a service called \""
						+serviceName+"\"");
				
				ServiceDef serviceDef = new ServiceDef(serviceInstance,method);
				
				// annotazioni dei parametri
				Annotation[][] paramsAnnotations = method.getParameterAnnotations();
				int paramIndex = 0;
				for (Annotation[] annotationsOfAParam : paramsAnnotations){
					Input input = search(annotationsOfAParam,Input.class);
					if (input==null){
						logger.error("The service \""+completeServiceName+"\" have a parameter without the Input annotation");
						throw new IllegalArgumentException("a parameter of method \""+method.getName()+"\" have not the input annotation");
					} else {
						String inputName = input.name();
						logger.debug("Found input called \""+inputName+"\" for service \""
								+serviceName+"\"");
					}
					Class<?> inputType = method.getParameterTypes()[paramIndex];
					serviceDef.addInput(input.name(), new InputDef(inputType,input.optional()));
					paramIndex++;
				}
				
				provider.add(serviceName, serviceDef);
			}
		}
		
		servicesProviders.put(providerName, provider);
	}
	
	@SuppressWarnings("unchecked")
	private <T extends Annotation> T search(Annotation[] annotations, Class<T> annotationType){
		for (Annotation a : annotations){
			if (annotationType.isInstance(a)){
				return (T)a;
			}
		}
		return null;
	}
	
	public boolean hasService(String providerName, String serviceName){
		if (servicesProviders.containsKey(providerName)){			
			ServiceProviderDef provider = servicesProviders.get(providerName);
			return provider.hasService(serviceName);
		} else {
			logger.debug("register don't know this provider (known providers: "+servicesProviders.keySet()+")");
			return false;
		}
	}
	
	public ServiceDef getService(String providerName, String serviceName){
		if (!hasService(providerName, serviceName)){
			throw new IllegalArgumentException();
		} else {
			return servicesProviders.get(providerName).getService(serviceName);
		}
	}	
	
}
